/**
 * @file cx_timer.c
 * @author Char_X (ziyangwork@163.com)
 * @brief implementation of timer trigger function with hardware independent(By register basic method)
 * @version 1.0
 * @date 2023-03-01 V0.1  Base Functions
 * @date 2023-03-18 V0.2  Added support for timestamp function,use macro CX_LIB_ENABLE_TIMER_TIMESTAMP
 * @date 2023-04-17 V0.3  Modified time stamp function,embedded in cx_timer;abolish cx_timer.Init 's parameter,return 0
 * 
 * @note: Starting hardware timer with calling member function:cx_timer.timerInit(InitHardwareTimer(args), args)
 *        call member function:cx_timer.INT_Handler() in hardware timer interrupt to activate cx_timer
 * 
 * @copyright Copyright (c) 2023
 * 
 */
#include "cx_timer1.h"
#include "cx_list.h"
#define NULL (void*)0

unsigned int tick = 0;

#ifndef CX_LIB_DISABLE_TIMER_TIMESTAMP
static unsigned int (*GetHardwareTimerCount)(void) = NULL;
static unsigned int HardCountPerTick = 0;
#endif

#define TASK_CONTINUE 0
#define TASK_REFRASH  1
#define TASK_ONESHOT  2

#define SW_CONTINUE   0
#define SW_REFRASH    1
#define SW_ONESHOT    2



#ifndef CX_DISABLE_TIMER_EVENT
timer_trigger_event_t event_head =
{
    .trigger_tick = 0xffffffff, //never been triggered
    .next = NULL,
    .event.value = 0,
    .trigger_type = TriggerTypeNormal
}, *event_cur_handler, *event_bk_handler;

cx_slist_t *event_list;
timer_trigger_event_t *event_current_handler, *event_backup_handler;

#endif

#ifndef CS_DISABLE_SW_TIMER
software_timer_t sw_head = 
{
    .trigger_tick = 0xffffffff,
    .next = NULL,
    .cb_handler = NULL,
    .trigger_type = TriggerTypeNormal
}, *sw_cur_handler, *sw_bk_handler;

cx_slist_t *swt_list;
software_timer_t *sw_current_handler, *sw_backup_handler;
#endif

static int nop_func(void *args)
{
    return 0;
}

#ifndef CX_LIB_DISABLE_TIMER_TIMESTAMP
static int timerInitTimestamp(unsigned int (*GetTimeCount)(void), unsigned int CountPerTick)
{
    GetHardwareTimerCount = GetTimeCount;
    HardCountPerTick = CountPerTick;
    return 0;
}

static unsigned int getTimestamp(void)
{
    return (tick * HardCountPerTick + GetHardwareTimerCount());
}
#endif

/**
 * @brief initialize the timer
 * 
 * @param TimerInit pre hook function
 * @param args hook function arguments
 * @return int status code
 */
static int timerInit(void)
{
    event_cur_handler = &event_head;
    sw_cur_handler = &sw_head;
    sw_head.cb_handler = nop_func;
    event_list = cx_slist_create();
    swt_list   = cx_slist_create();
    return 0;
}


#ifndef CX_DISABLE_TIMER_EVENT
static int addTimerTriggerEvent(timer_trigger_event_t *trigger_event)
{
    cx_slist_item_t *item = cx_slist_item_create();
    timer_trigger_event_t *cur = &event_head;
    item->content = (cx_pointer_t)trigger_event;
    cx_slist_append(event_list, item);
    trigger_event->trigger_tick = tick + trigger_event->gap;
    return 0;
}

static int deleteTimerTriggerEvent(timer_trigger_event_t *trigger_event)
{
    timer_trigger_event_t *cur = &event_head, *pre = &event_head;
    while((cur != trigger_event) && (cur != NULL))
    {
        pre = cur;
        cur = cur->next;
    }
    if(cur == NULL)
    {
        return 1;
    }
    pre->next = cur->next;
    cur->next = NULL;
    return 0;

}

static int TimerTriggerEventHandler(timer_trigger_event_t *trigger_event)
{
    int ret = TASK_CONTINUE;
    if(trigger_event->trigger_tick <= tick)
    {
        event.value |= trigger_event->event.value;
        trigger_event->trigger_tick += trigger_event->gap;
        if(trigger_event->trigger_type == TriggerTypeOneShot)
        {
            ret = TASK_ONESHOT;
            return ret;
        }
    }
    if(trigger_event->next == NULL)
    {
        ret = TASK_REFRASH;
    }
    return ret;
}

static cx_u32 TimerGetTicks(void)
{
    return tick;
}

static int EventHandler(void)
{
    int tmp = TimerTriggerEventHandler(event_cur_handler);
    if(tmp == TASK_CONTINUE)
    {
        event_cur_handler = event_cur_handler->next;
    }
    else if(tmp == TASK_REFRASH)
    {
        event_cur_handler = &event_head;
    }
    else if(tmp == TASK_ONESHOT)
    {
        if(event_cur_handler->next == NULL)
        {
            event_bk_handler = &event_head;
        }
        else
        {
            event_bk_handler = event_cur_handler->next;
        }
        deleteTimerTriggerEvent(event_cur_handler);
        event_cur_handler = event_bk_handler;
    }
    return 0;
}

#endif

/*****************************************************************/

#ifndef CX_DISABLE_SW_TIMER
static int regSoftwareTimer(software_timer_t *sw_timer)
{
    software_timer_t *cur = &sw_head;
    while(cur->next)
    {
        cur = cur->next;
    }
    cur->next = sw_timer;
    sw_timer->gap = tick + sw_timer->gap;
    sw_timer->next = NULL;

    return 0;
}

static int logoutSoftwareTimer(software_timer_t *sw_timer)
{
    software_timer_t *cur = &sw_head, *pre = &sw_head;
    while((cur != sw_timer) && (cur != NULL))
    {
        if(cur == NULL)
        {
            break;
        }
        pre = cur;
        cur = cur->next;
    }
    if(cur == NULL)
    {
        return -1;
    }
    else
    {
        pre->next = cur->next;
        cur->next = NULL;
    }

    return 0;
}

static int SoftwareTimerHandler(software_timer_t *sw_timer)
{
    int ret = SW_CONTINUE;
    if(sw_timer->trigger_tick <= tick)
    {
        sw_timer->cb_handler(sw_timer->args);
        sw_timer->trigger_tick = sw_timer->trigger_tick + sw_timer->gap;
        if(sw_timer->trigger_type == TriggerTypeOneShot)
        {
            ret = SW_ONESHOT;
            return ret;
        }
    }
    if(sw_timer->next == NULL)
    {
        ret = SW_REFRASH;
    }

    return ret;
}

static void SoftHandler(void)
{
    int tmp = SoftwareTimerHandler(sw_cur_handler);
    if(tmp == SW_CONTINUE)
    {
        sw_cur_handler = sw_cur_handler->next;
    }
    else if (tmp == SW_REFRASH)
    {
        sw_cur_handler = &sw_head;
    }
    else if (tmp == SW_ONESHOT)
    {
        if(sw_cur_handler->next == NULL)
        {
            sw_bk_handler = &sw_head;
        }
        else
        {
            sw_bk_handler = sw_cur_handler->next;
        }
        logoutSoftwareTimer(sw_cur_handler);
        sw_cur_handler = sw_bk_handler;
    }
}
#endif


static void INT_Handler(void)
{
#ifndef CX_DISABLE_TIMER_EVENT
    EventHandler();
#endif

#ifndef CX_DISABLE_SW_TIMER
    SoftHandler();
#endif
    ++tick;
} 

timer_interface_t cx_timer = 
{
    .Init               = timerInit,
    .getTicks          = TimerGetTicks,
    .InterruptHandler   = INT_Handler,
    
#ifndef CX_DISABLE_TIMER_EVENT
    .deleteTriggerEvent = deleteTimerTriggerEvent,
    .addTriggerEvent    = addTimerTriggerEvent,
#endif

#ifndef CX_DISABLE_SW_TIMER
    .regSWTimer         = regSoftwareTimer,
    .unSWTimer          = logoutSoftwareTimer,
#endif

#ifndef CX_LIB_DISABLE_TIMER_TIMESTAMP
    .timestamp_init = timerInitTimestamp,
    .getTimeStamp = getTimestamp,
#endif

};

